#include "refine/smooth_despill_a.h"
#include "smooth_despill_impl.h"

namespace refine {
namespace smooth_despill {
namespace a {

template <typename Raster>
class alpha_smoother : public alpha_smoother_base
{
public:
	alpha_smoother(
		Raster const & im_, 
		Raster & next_im_, ///< it receives updates in alpha channel
		img::bitmap & modifiable, 
		float max_condition_number/*, float more_contrast*/)
		: alpha_smoother_base(modifiable, max_condition_number/*, more_contrast*/), 
		  im(im_), next_im(next_im_)
	{
		clear(); 
	}
	void clear() {
		remove_all();
		checks = achanges = 0;
	}
	void add(size_point const & p)
		{ s += im[p]; }
	void remove(size_point const & p)
		{ s -= im[p]; }
	void remove_all() 
		{ s.clear(); }
	void process(size_point const & p, size_rect const &) 
	{
		++checks;

		vectorf F, B;
		float condition_number;
		if (!s.means(max_condition_number, F, B, &condition_number)) {
			modifiable[p] = false;
			return;
		}

		vectorf I = v(im[p]);
		vectorf d0 = I - B;
		vectorf d1 = I - F;

		smatrixf s = tsquare(d0) + tsquare(d1);
		float lambda;
		s.eigenvalues(0, 0, &lambda);
		vectorf n;
		n = s.eigenvector(lambda, true);
		float t0 = -scalar_product(d0,n);
		float t1 = -scalar_product(d1,n);
		unsigned alpha = classify_end(condition_number, t0, t1);

		if (alpha == im[p].a) {
			modifiable[p] = false;
			return;
		}

		next_im[p].a = alpha;
		++achanges;
	}
private:
	Raster const & im;
	Raster & next_im;
	summator s;
};

template <typename Raster>
static void perform_core(
	Raster & im, 
	img::bitmap & modifiable, ///< map of pixels that may be changed during the following pass
	const params & p)
{
	if (!p.silent)
		clog << "Smooth-A & Despill" << endl;

	size_extent dim = im.dim();
	assert (dim == modifiable.dim());

	Raster despilled = im;

	//smooth alpha passes
	for (uint pass = 0; pass < p.passes_num; ++pass) {
		if (!p.silent)
			clog << "pass " << pass+1 << '/' << p.passes_num << flush;

		alpha_smoother<Raster> sm(im, despilled, modifiable, p.max_condition_number/*, p.more_contrast*/);
		window_pass(sm, p.whs);

		if (!p.silent)
			clog << ": checked = " << sm.check_count() << ", alpha changes = " << sm.change_count() << endl;

		if (sm.change_count() > 0)
			copy_alpha(im, despilled);
		else 
			break;

		if (pass+1 < p.passes_num)
			img::dilate8(modifiable, p.whs);
	}

	// no smoothing, despill only
	despiller<Raster> de(im, despilled, p.max_condition_number);
	window_pass(de, p.whs);
	im = despilled;
}

template <typename Raster>
static void perform_from_grey_mask(
	Raster & im, 
	const params & p)
{
	size_extent dim = im.dim();

	img::bitmap not_min(dim), not_max(dim);
	for (typename Raster::const_iterator i = im.begin(); i != im.end(); ++i) {
		not_min[i] = (*i).a != 0;
		not_max[i] = (*i).a != uchar_max;
	}

	img::bitmap modifiable;
	{
		uint w = 1 + p.whs * (p.max_condition_number - 1) / (p.max_condition_number + 1);
		img::dilate4(modifiable, not_min, w);
		img::bitmap tmp;
		img::dilate4(tmp, not_max, w);
		img::combine(modifiable, tmp, img::bit_and());
	}

	perform_core(im, modifiable, p);
}

// exported functions

void perform(
	byte_image & im, 
	const params & p)
{
	perform_from_grey_mask(im, p);
}

void perform(
	argb_byte_image & im, 
	const params & p)
{
	perform_from_grey_mask(im, p);
}


} //namespace a
} //namespace smooth_despill
} //namespace refine
